﻿using System;
using System.Collections.Generic;
using Castle.Core;
using Castle.Core.Configuration;
using Castle.Core.Interceptor;
using Castle.MicroKernel;
using Castle.MicroKernel.ModelBuilder;
using Castle.Facilities.FactorySupport;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using FluentIoC.Interfaces;

namespace FluentIoC.Windsor
{
    public class WindsorIoCConfiguration : IIoCConfiguration
    {
        private bool _initializedFactorySupport;
        private Dictionary<Type, string> _factories = new Dictionary<Type, string>();
        

        protected IWindsorContainer _container;

        public IContainer Container
        {
            get { return _container as IContainer; }
        }

        public WindsorIoCConfiguration(IWindsorContainer container)
        {
            _container = container;
        }

        public void RegisterComponent(string name, Type component)
        {
            _container.Kernel.AddComponent(name, component);
        }

        public void RegisterService(string name, Type service, Type component)
        {
            _container.Kernel.AddComponent(name, service, component);
        }

        public void RegisterComponent(
            string key, 
            Type component,
            Dictionary<string, string> parameters, 
            Lifestyle lifestyle, 
            Type factory, 
            string factoryCreate)
        {
            throw new System.NotImplementedException();
        }


        public void RegisterService(
            string name, 
            Type service, 
            Type component, 
            Dictionary<string, string> parameters, 
            Lifestyle lifestyle,
            Type factory, 
            string factoryCreate)
        {
            var componentModel = GetComponentModel(_container.Kernel, name, service, component);

            if(null != parameters)
            {
                AddParameters(componentModel, parameters);
            }

            if(Lifestyle.Undefined != lifestyle)
            {
                AddLifeStyle(componentModel, lifestyle);
            }

            if(null != factory && false == string.IsNullOrEmpty(factoryCreate))
            {
                if(false == _factories.ContainsKey(factory))
                {
                    throw new Exception("Factory has not been registered");
                }

                var config = new MutableConfiguration(name);

                config.Attributes["factoryCreate"] = factoryCreate;
                config.Attributes["factoryId"] = _factories[factory];

                _container.Kernel.ConfigurationStore.AddComponentConfiguration(name, config);
                _container.Kernel.AddComponent(name, service, component);
            }
            else
            {
                _container.Kernel.Register(
                    Component.For(
                        componentModel
                    )
                );
            }
        }

        public void RegisterFactory(string name, Type type)
        {
            if(_factories.ContainsKey(type))
            {
                if(_factories[type] != name)
                {
                    throw new Exception(
                        string.Format("The factory with the key {0} has already been registered with the name {1}",
                            name,
                            _factories[type]
                        )
                    );
                }
            }
            else
            {
                if (false == _initializedFactorySupport)
                {
                    _container.AddFacility("factories", new FactorySupportFacility());
                    _container.AddComponent("standard.interceptor", typeof(StandardInterceptor));
                    _initializedFactorySupport = true;
                }

                _factories[type] = name;
                _container.AddComponent(name, type);
            }
        }

        private static ComponentModel GetComponentModel(IKernel kernel, string name, Type service, Type component)
        {
            var componentModelBuilder = new DefaultComponentModelBuilder(kernel);
            return componentModelBuilder.BuildModel(name, service, component, null);
        }

        private void AddParameters(ComponentModel componentModel, IDictionary<string, string> parameters)
        {
            foreach (var p in parameters)
            {
                componentModel.Parameters.Add(p.Key, p.Value);
            }
        }

        private void AddLifeStyle(ComponentModel componentModel, Lifestyle lifestyle)
        {
            componentModel.LifestyleType = (LifestyleType)Enum.Parse(typeof(LifestyleType), lifestyle.ToString());
        }
    }
}
